/* 
 * BaseAction.java
 * 
 * Data de criação: 09/04/2010
 * 
 * CORPORATIVO - MTE. 
 *
 */
package br.com.o2solutions.siso.webapp.action;

import java.io.Serializable;
import java.util.List;

import javax.faces.context.FacesContext;

import net.vidageek.mirror.dsl.Mirror;

import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.core.Events;
import org.jboss.seam.international.StatusMessage.Severity;
import org.jboss.seam.international.StatusMessages;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import br.com.o2solutions.core.integration.repository.entity.BaseEntity;
import br.com.o2solutions.core.service.Service;

/**
 * 
 * Classe responsável por abstrair as actions (fronteiras) para a aplicação.
 * 
 * @author osvaldeir
 * @since 30/08/2010
 * 
 * @param <T>
 *            Tipo de entidade a ser referenciada pela action
 * @param <S>
 *            Tipo de Service para a Entidade
 */
@Scope(ScopeType.CONVERSATION)
public abstract class BaseAction<T extends BaseEntity<?>, S extends Service<T>>
		implements Serializable {

	private static final long serialVersionUID = -8628960084504144407L;

	public static final String SUCCESS_AND_END_CONVERSATION = "successAndEndConversation";

	public static final String LINKED_ENTITY_ID = "ENTITY_ID";

	@In(create = true)
	protected StatusMessages statusMessages;
	
	protected Logger log = LoggerFactory.getLogger(this.getClass());

	private T entity;

	private S service;

	protected List<T> resultList;

	/**
	 * Retorna o(a) resultList.
	 * 
	 * @return List<T>
	 */
	public List<T> getResultList() {
		if (resultList == null) {
			resultList = listAll();
		}
		return resultList;
	}

	/**
	 * Atribui o(a) resultList.
	 * 
	 * @param resultList
	 *            List<T>
	 */
	public void setResultList(List<T> resultList) {
		this.resultList = resultList;
	}

	/**
	 * Retorna o(a) entity.
	 * 
	 * @return T Entidade
	 */
	public T getEntity() {
		if (entity == null) {
			entity = createNewEntity();
		}
		return entity;
	}

	/**
	 * Retorna uma nova instância da Entidade Referencia do Serviço
	 * 
	 * @return T Entidade
	 */
	@SuppressWarnings("unchecked")
	protected T createNewEntity() {
		final Class<?> genericType = new Mirror().on(this.getClass()).reflect()
				.parentGenericType().atPosition(0);
		return (T) new Mirror().on(genericType).invoke().constructor()
				.withoutArgs();
	}

	/**
	 * Atribui o(a) entity.
	 * 
	 * @param entity
	 *            T
	 */
	public void setEntity(T entity) {
		this.entity = entity;
	}

	/**
	 * Retorna o(a) service.
	 * 
	 * @return SiliparService<T>
	 */
	public Service<T> getService() {
		return service;
	}

	/**
	 * Atribui o(a) service.
	 * 
	 * @param service
	 *            SiliparService<T>
	 */
	public void setService(S service) {
		this.service = service;
	}

	/**
	 * Cadastra a Entidade representada pela visao na base de dados
	 */

	public String save() {
		getService().save(getEntity());
		resetEntity();
		showSuccessMessage();
		Events.instance().raiseEvent(getObserverSavedEvent());
		return SUCCESS_AND_END_CONVERSATION;
	}

	/**
	 * Remove a Entidade reprensentada pela View na base de dados
	 */
	public String remove() {
		getService().remove(getEntity());
		resetEntity();
		showSuccessMessage();
		Events.instance().raiseEvent(getObserverRemovedEvent());
		return SUCCESS_AND_END_CONVERSATION;
	}

	/**
	 * Atualiza uma entidade na base de dados.
	 * 
	 * @return string valor de navegação.
	 */
	public String update() {
		getService().update(getEntity());
		resetEntity();
		showSuccessMessage();
		Events.instance().raiseEvent(getObserverUpdatedEvent());
		return SUCCESS_AND_END_CONVERSATION;
	}

	/**
	 * Lista todas as Entidades representadas por este Objeto
	 * 
	 * @return List<T>
	 */
	public List<T> listAll() {
		return getService().findAll();
	}

	/**
	 * Atribui nulo ao resultList para que na proxima chamada do método
	 * getResultList seja inicializada novamente através do método listAll.
	 */
	public void updateResultList() {
		setResultList(null);
	}
	
	/**
	 * Nos casos em que a validação falha e a lista deve ser atualizada visto a modificação dos objetos desta.
	 */
	@Observer("br.com.o2solutions.siso.validationFailed")
	public final void resetResultList(){
		setResultList(null);
	}

	/**
	 * Reinicia a entidade referencia para a view
	 */
	public void resetEntity() {
		setEntity(createNewEntity());
	}

	/**
	 * Aciona o interceptador do Seam para que a action execute os processos de
	 * inicialização.
	 */
	@Create
	public final void create() {
		doCreate();
	}

	/**
	 * Realiza o processo de inicialização da view quanto esta esta sendo criada
	 */
	public void doCreate() {
	}

	/**
	 * Apresenta uma mensagem de sucesso de operação (mensagem padrão)
	 */
	protected void showSuccessMessage() {
		showInfoMessage("messages.success");
	}

	/**
	 * Exibe a mensagem nos casos de falha (mensagem padrão)
	 */
	protected void showFaluireMessage() {
		showFaliureMessage("messages.faliure");
	}

	/**
	 * Apresenta uma mensagem informativa
	 * 
	 * @param message
	 *            Mensagem ou chave da mensagem a ser exibida
	 */
	protected void showInfoMessage(String message) {
		statusMessages.addFromResourceBundle(Severity.INFO, message);
	}

	/**
	 * Apresenta uma mensagem de erro
	 * 
	 * @param message
	 *            Mensagem ou chave da mensagem a ser exibida
	 */
	protected void showFaliureMessage(String message) {
		statusMessages.addFromResourceBundle(Severity.ERROR, message);
	}

	/**
	 * Retorna o evento para ação do observer após a persistência.
	 * 
	 * @return String
	 */
	public final String getObserverSavedEvent() {
		return this.getClass().getName().concat("_SAVED");
	}

	/**
	 * Retorna o evento para ação do observer após a remoção.
	 * 
	 * @return String
	 */
	public final String getObserverRemovedEvent() {
		return this.getClass().getName().concat("_REMOVED");
	}

	/**
	 * Retorna o evento para ação do observer após a atualização.
	 * 
	 * @return String
	 */
	public final String getObserverUpdatedEvent() {
		return this.getClass().getName().concat("_UPDATED");
	}

	/**
	 * Carrega a Entidade através de um link ou acao que envie um parametro.
	 */
	public abstract void loadEntityByLinkId();

	/**
	 * Retorna o Id enviado via parametro na Solicitação. O nome do parametro
	 * deve ser 'ENTITY_ID'
	 * 
	 * @return String
	 */
	public String getCurrentEntityLinkedId() {
		return FacesContext.getCurrentInstance().getExternalContext()
				.getRequestParameterMap().get(LINKED_ENTITY_ID);
	}
}
